{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 3000 entries, 0 to 2999\n",
      "Data columns (total 12 columns):\n",
      " #   Column  Non-Null Count  Dtype \n",
      "---  ------  --------------  ----- \n",
      " 0   公交      3000 non-null   int64 \n",
      " 1   写字楼     3000 non-null   int64 \n",
      " 2   医院      3000 non-null   int64 \n",
      " 3   商场      3000 non-null   int64 \n",
      " 4   地铁      3000 non-null   int64 \n",
      " 5   学校      3000 non-null   int64 \n",
      " 6   小区名字    3000 non-null   object\n",
      " 7   建造时间    3000 non-null   int64 \n",
      " 8   房型      3000 non-null   object\n",
      " 9   楼层      3000 non-null   int64 \n",
      " 10  每平米价格   3000 non-null   int64 \n",
      " 11  面积      3000 non-null   int64 \n",
      "dtypes: int64(10), object(2)\n",
      "memory usage: 281.4+ KB\n",
      "None\n",
      "公交       0\n",
      "写字楼      0\n",
      "医院       0\n",
      "商场       0\n",
      "地铁       0\n",
      "学校       0\n",
      "小区名字     0\n",
      "建造时间     0\n",
      "房型       0\n",
      "楼层       0\n",
      "每平米价格    0\n",
      "面积       0\n",
      "dtype: int64\n"
     ]
    }
   ],
   "source": [
    "import pandas as pd\n",
    "\n",
    "df = pd.read_csv('../data/challenge-1-beijing.csv')\n",
    "df.head(5)  # 预览前 5 行数据\n",
    "\n",
    "# 检查数据基本信息\n",
    "print(df.info())\n",
    "\n",
    "# 检查缺失值\n",
    "print(df.isnull().sum())\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 88,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>公交</th>\n",
       "      <th>写字楼</th>\n",
       "      <th>医院</th>\n",
       "      <th>商场</th>\n",
       "      <th>地铁</th>\n",
       "      <th>学校</th>\n",
       "      <th>小区名字</th>\n",
       "      <th>建造时间</th>\n",
       "      <th>房型</th>\n",
       "      <th>楼层</th>\n",
       "      <th>面积</th>\n",
       "      <th>每平米价格</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>18</td>\n",
       "      <td>18</td>\n",
       "      <td>10</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>49</td>\n",
       "      <td>远洋山水</td>\n",
       "      <td>2006</td>\n",
       "      <td>2室1厅</td>\n",
       "      <td>26</td>\n",
       "      <td>96</td>\n",
       "      <td>60937</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>17</td>\n",
       "      <td>42</td>\n",
       "      <td>10</td>\n",
       "      <td>0</td>\n",
       "      <td>4</td>\n",
       "      <td>37</td>\n",
       "      <td>椿树园</td>\n",
       "      <td>1998</td>\n",
       "      <td>3室1厅</td>\n",
       "      <td>14</td>\n",
       "      <td>130</td>\n",
       "      <td>88686</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>18</td>\n",
       "      <td>36</td>\n",
       "      <td>9</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>24</td>\n",
       "      <td>永乐小区</td>\n",
       "      <td>1989</td>\n",
       "      <td>3室1厅</td>\n",
       "      <td>18</td>\n",
       "      <td>74</td>\n",
       "      <td>46621</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>15</td>\n",
       "      <td>49</td>\n",
       "      <td>13</td>\n",
       "      <td>0</td>\n",
       "      <td>2</td>\n",
       "      <td>45</td>\n",
       "      <td>主语家园</td>\n",
       "      <td>2007</td>\n",
       "      <td>4室3厅</td>\n",
       "      <td>2</td>\n",
       "      <td>462</td>\n",
       "      <td>86147</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>6</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>天伦锦城</td>\n",
       "      <td>2007</td>\n",
       "      <td>1室1厅</td>\n",
       "      <td>13</td>\n",
       "      <td>64</td>\n",
       "      <td>42500</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   公交  写字楼  医院  商场  地铁  学校  小区名字  建造时间    房型  楼层   面积  每平米价格\n",
       "0  18   18  10   0   2  49  远洋山水  2006  2室1厅  26   96  60937\n",
       "1  17   42  10   0   4  37   椿树园  1998  3室1厅  14  130  88686\n",
       "2  18   36   9   0   1  24  永乐小区  1989  3室1厅  18   74  46621\n",
       "3  15   49  13   0   2  45  主语家园  2007  4室3厅   2  462  86147\n",
       "4   6    0   0   0   0   0  天伦锦城  2007  1室1厅  13   64  42500"
      ]
     },
     "execution_count": 88,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## 代码开始 ### (≈ 1 行代码)\n",
    "features = df[['公交', '写字楼', '医院', '商场', '地铁', '学校', '小区名字', '建造时间', '房型', '楼层', '面积']]\n",
    "target = df['每平米价格']\n",
    "## 代码结束 ###\n",
    "\n",
    "pd.concat([features, target], axis=1).head()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((2100, 11), (2100,), (900, 11), (900,))"
      ]
     },
     "execution_count": 99,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 70% 训练集，30% 测试集\n",
    "\n",
    "# 1.\n",
    "split_num = int(len(df)*0.7) # 70% 分割数\n",
    "\n",
    "### 代码开始 ### (≈ 4 行代码)\n",
    "X_train = features[:split_num]\n",
    "y_train = target[:split_num]\n",
    "X_test = features[split_num:]\n",
    "y_test = target[split_num:]\n",
    "### 代码结束 ###\n",
    "\n",
    "\n",
    "# 默认输出 或 len(X_train), len(y_train), len(X_test), len(y_test)\n",
    "X_train.shape, y_train.shape, X_test.shape, y_test.shape\n",
    "\n",
    "\n",
    "# 问题\n",
    "# - 数据集划分方式是直接按行切分，这可能导致训练集和测试集的分布不均（如果数据是按时间或其他顺序排列的）。\n",
    "# - 没有使用随机划分或交叉验证。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((2100, 11), (2100,), (900, 11), (900,))"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "# 2. 这种方法更好\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "X_train, X_test, y_train, y_test = train_test_split(features, target, test_size=0.3, random_state=42)\n",
    "\n",
    "X_train.shape, y_train.shape, X_test.shape, y_test.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([  5.43132703,  10.30822592, 116.01707075]), 1263)"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn.linear_model import LinearRegression # type: ignore\n",
    "\n",
    "\n",
    "\n",
    "# 对非数值特征进行独热编码\n",
    "features_encoded = pd.get_dummies(features, columns=['小区名字', '房型'], drop_first=True)\n",
    "\n",
    "## 代码开始 ### (≈ 2 行代码)\n",
    "model = LinearRegression()\n",
    "model.fit(features_encoded, target)  # 使用编码后的特征和目标变量训练模型\n",
    "\n",
    "## 代码结束 ###\n",
    "\n",
    "# 得到模型拟合参数\n",
    "model.intercept_, model.coef_\n",
    "\n",
    "model.coef_[:3], len(model.coef_)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MAPE: 6.06%\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "def mape(y_true, y_pred):\n",
    "    \"\"\"\n",
    "    参数:\n",
    "    y_true -- 测试集目标真实值\n",
    "    y_pred -- 测试集目标预测值\n",
    "    \n",
    "    返回:\n",
    "    mape -- MAPE 评价指标\n",
    "    \"\"\"\n",
    "    \n",
    "    ### 代码开始 ### (≈ 2 行代码)\n",
    "    mape = np.mean(np.abs((y_true - y_pred) / y_true)) * 100\n",
    "    ### 代码结束 ###\n",
    "    \n",
    "    return mape\n",
    "\n",
    "# 确保对测试集进行与训练集相同的独热编码, 避免汉字value特征值的问题\n",
    "X_test_encoded = pd.get_dummies(X_test, columns=['小区名字'], drop_first=True)\n",
    "\n",
    "# 对齐测试集和训练集的特征\n",
    "X_test_encoded = X_test_encoded.reindex(columns=features_encoded.columns, fill_value=0)\n",
    "\n",
    "# 预测并计算 MAPE\n",
    "y_true = y_test.values\n",
    "y_pred = model.predict(X_test_encoded)\n",
    "mape_value = mape(y_true, y_pred)\n",
    "print(f\"MAPE: {mape_value:.2f}%\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
